package com.hdu.joinInMemory;

import com.alibaba.fastjson.JSON;
import com.hdu.joinInMemory.entity.DO.Address;
import com.hdu.joinInMemory.entity.DO.Order;
import com.hdu.joinInMemory.entity.DO.Product;
import com.hdu.joinInMemory.entity.DO.User;
import com.hdu.joinInMemory.entity.VO.*;
import com.hdu.joinInMemory.repository.AddressRepository;
import com.hdu.joinInMemory.repository.OrderRepository;
import com.hdu.joinInMemory.repository.ProductRepository;
import com.hdu.joinInMemory.repository.UserRepository;
import com.hdu.joinInMemory.service.JoinService;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@SpringBootApplication
@RestController
public class JoinInMemoryApplication {

    private final OrderRepository orderRepository;
    private final JoinService joinService;
    private final UserRepository userRepository;
    private final ProductRepository productRepository;
    private final AddressRepository addressRepository;


    public JoinInMemoryApplication(OrderRepository orderRepository,
                                   JoinService joinService,
                                   UserRepository userRepository,
                                   ProductRepository productRepository,
                                   AddressRepository addressRepository) {
        this.orderRepository = orderRepository;
        this.joinService = joinService;
        this.userRepository = userRepository;
        this.productRepository = productRepository;
        this.addressRepository = addressRepository;
    }

    public static void main(String[] args) {
        SpringApplication.run(JoinInMemoryApplication.class, args);
    }

    @GetMapping("test")
    public String test() {
        System.out.println("内存Join : ");
        System.out.println(testJoinInMemory());
        System.out.println("批量查询 ：");
        System.out.println(testBatchQueryVersion());
        System.out.println("forEach 查询");
        System.out.println(testForEachVersion());
        return "hello world";
    }

    private String testJoinInMemory() {
        // 1, 查询 userId = 1 的所有订单
        List<Order> orders = orderRepository.getOrderByUserId(1L);
        // 2, order -> orderDetailVO
        List<OrderDetailVO> orderDetailVOS = orders.stream()
                .map(order -> new OrderDetailVO(OrderVO.apply(order)))
                .collect(Collectors.toList());
        // 3, 内存Join
        joinService.joinInMemory(orderDetailVOS);
        return JSON.toJSONString(orderDetailVOS);
    }

    private String testBatchQueryVersion() {
        // 1, 查询 userId = 1 的所有订单
        List<Order> orders = orderRepository.getOrderByUserId(1L);

        // 2, 收集 userIds
        List<Long> userIds = orders.stream()
                .map(Order::getUserId)
                .distinct()
                .collect(Collectors.toList());
        // 3, 批量查询User
        List<User> users = userRepository.findUserById(userIds);

        // 根据 userId 进行分组 转化成 Map<UserId, List<UserVO>>
        Map<Long, List<UserVO>> userId2UserVO = users.stream()
                .map(UserVO::apply)
                .collect(Collectors.groupingBy(UserVO::getId));

        // 4, 收集 productId
        List<Long> productIds = orders.stream()
                .map(Order::getProductId)
                .distinct()
                .collect(Collectors.toList());

        // 5, 批量查询Product
        List<Product> products = productRepository.findProductById(productIds);

        // 根据 productId 进行分组 转化成 Map<productId, List<Product>>
        Map<Long, List<ProductVO>> productId2ProductVO = products.stream()
                .map(ProductVO::apply)
                .collect(Collectors.groupingBy(ProductVO::getId));

        // 6, 收集 addressId
        List<Long> addressIds = orders.stream()
                .map(Order::getAddressId)
                .distinct()
                .collect(Collectors.toList());
        // 7, 批量查询Addresses
        List<Address> addresses = addressRepository.findAddressById(addressIds);

        // 根据 addressId 进行分组 转化成 Map<addressId, List<Address>>
        Map<Long, List<AddressVO>> addressid2AddressVO = addresses.stream()
                .map(AddressVO::apply)
                .collect(Collectors.groupingBy(AddressVO::getId));

        // 8, 遍历每条订单 生成OrderDetailVO
        List<OrderDetailVO> orderDetailVOS = orders.stream().map(order -> {
            OrderDetailVO orderDetailVO = new OrderDetailVO(OrderVO.apply(order));
            Long userId = order.getUserId();
            Long productId = order.getProductId();
            Long addressId = order.getAddressId();

            orderDetailVO.setUser(userId2UserVO.get(userId).get(0));
            orderDetailVO.setProduct(productId2ProductVO.get(productId).get(0));
            orderDetailVO.setAddress(addressid2AddressVO.get(addressId).get(0));
            return orderDetailVO;
        }).collect(Collectors.toList());

        return JSON.toJSONString(orderDetailVOS);
    }

    private String testForEachVersion() {
        // 1, 查询 userId = 1 的所有订单
        List<Order> orders = orderRepository.getOrderByUserId(1L);

        // 2, 遍历每条订单 生成OrderDetailVO
        List<OrderDetailVO> orderDetailVOS = orders.stream().map(order -> {
            // 获得userId
            Long userId = order.getUserId();
            // 获得user
            User user = userRepository.findUserById(userId);

            // 获得addressId
            Long addressId = order.getAddressId();
            // 获得address
            Address address = addressRepository.findAddressById(addressId);

            // 获得productId
            Long productId = order.getProductId();
            // 获得product
            Product product = productRepository.findProductById(productId);

            // 封装成OrderVO
            OrderDetailVO orderDetailVO = new OrderDetailVO(OrderVO.apply(order));
            orderDetailVO.setUser(UserVO.apply(user));
            orderDetailVO.setAddress(AddressVO.apply(address));
            orderDetailVO.setProduct(ProductVO.apply(product));
            return orderDetailVO;
        }).collect(Collectors.toList());

        return JSON.toJSONString(orderDetailVOS);
    }

}
